﻿#region Copyright(C) 2018 FI Ltd. All rights reserved.
// ==================================================================================================
// Copyright(C) 2018 FI Ltd. All rights reserved.
//
// SYSTEM NAME	    : FI.Net Application Framework
// COMPONENT ID	    : FI.Core
// COMPONENT DESC   : FI.Net Core Foundation
//
// CREATED DATE/BY  : 18 Aug 2018 / Tian Mi
//
// REVISION HISTORY :	
// DATE/BY			SR#/CS/PM#/OTHERS		DESCRIPTION OF CHANGE
// ==================================================================================================
#endregion

using FI.Core.Properties;
using FI.Core.Utilities;
using System;
using System.Reflection;

namespace FI.Core
{
    /// <summary>
    /// Provides a wrapper in which the given <see cref="EventHandler"/> is executed.
    /// </summary>
    public class EventHandlerWrapper
    {
        #region Fields
        internal Delegate wrappedEventHandler;
        internal EventHandler newEventHandler;
        internal Action<Action> wrapper;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="EventHandlerWrapper"/> class.
        /// </summary>
        /// <param name="eventHandler">The <see cref="Delegate"/> wrapped.</param>
        /// <param name="wrapper">The <see cref="Action"/> to wrap the <see cref="EventHandler"/>.</param>
        public EventHandlerWrapper(Delegate eventHandler, Action<Action> wrapper)
        {
            Guard.ArgumentNotNull(eventHandler, "eventHandler");
            Guard.ArgumentNotNull(wrapper, "wrapper");

            wrappedEventHandler = eventHandler;
            this.wrapper = wrapper;
            ParameterInfo[] parameters;
            if (!EventHandlerConverter.IsValidEventHandler(eventHandler.GetType(), out parameters))
            {
                throw new InvalidOperationException(ResourceUtility.Format(Resources.ExceptionNotEventHandler, eventHandler.GetType().FullName));
            }
            newEventHandler+= Invoke;
        }
        #endregion

        #region Operators
        /// <summary>
        /// Performs an implicit conversion from <see cref="FI.Core.EventHandlerWrapper"/> to <see cref="System.EventHandler"/>.
        /// </summary>
        /// <param name="eventHandlerWrapper">The event handler wrapper.</param>
        /// <returns>
        /// The result of the conversion.
        /// </returns>
        public static implicit operator Delegate(EventHandlerWrapper eventHandlerWrapper)
        {
            Guard.ArgumentNotNull(eventHandlerWrapper, "eventHandlerWrapper");
            return eventHandlerWrapper.ConvertTo(eventHandlerWrapper.wrappedEventHandler.GetType());
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Converts to.
        /// </summary>
        /// <param name="eventHandlerType">Type of the event handler.</param>
        /// <returns>The new <see cref="Delegate"/> of the type to convert.</returns>
        public Delegate ConvertTo(Type eventHandlerType)
        {
            Guard.ArgumentNotNull(eventHandlerType, "eventHandlerType");
            Delegate eventHandler;
            if (EventHandlerConverter.TryConvert(newEventHandler, eventHandlerType, out eventHandler))
            {
                return eventHandler;
            }
            throw new InvalidOperationException(ResourceUtility.Format(Resources.ExceptionNotEventHandler, eventHandlerType.FullName)); 
        }
        #endregion

        #region Protected methods
        /// <summary>
        /// Invokes the <see cref="EventHandler"/> in wrapper.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void Invoke(object sender, EventArgs args)
        {
            Action invokeHandler =
                    () => this.wrappedEventHandler.Method.Invoke(this.wrappedEventHandler.Target, new object[] { sender, args });
            wrapper(invokeHandler);
        }
        #endregion
    }
}
